#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/**
  * 函    数：按键初始化
  * 参    数：无
  * 返 回 值：无
  */
void Key_Init(void)
{
	/*1、开启GPIOB的时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);		
    
    /**
    2、要使用gpio引脚作为外部中断EXTI，那么必须开启 AFIO 时钟
    参考数据手册
    10.2.5 External interrupt/event line mapping
    The 112 GPIOs are connected to the 16 external interrupt/event lines in the following
    manner
    
    To configure the AFIO_EXTICRx for the mapping of external interrupt/event lines onto GPIOs, the AFIO
    clock should first be enabled. Refer to Section 7.3.7: APB2 peripheral clock enable register
    (RCC_APB2ENR) for low-, medium-, high- and XL-density devices
    */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		

	
	/*
    3、GPIO初始化
    参考stm32编程手册    9.1.3 External interrupt/wakeup lines
        All ports have external interrupt capability. 
        To use external interrupt lines, the port must be
        configured in input mode
    */
	GPIO_InitTypeDef GPIO_InitStructure;
    /**
    * 将PB10和PB11引脚初始化为上拉输入，
    * 因为外部没有上拉电阻（所以不能配置为浮空输入）
    */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    /*    
        PB10  -> K1
        PB11  -> K2
    */
	GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    /**
        PA11  -> K3
        PA12  -> K4
    */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 4、使用 GPIO_EXTILineConfig() 来映射GPIO到EXTI线
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,10);
    /**
        PB11 和 PA11 共享了一个中断行
        所以只有其中一个可以触发中断
        谁后调用GPIO_EXTILineConfig配置，谁就能触发中断
    */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,11);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,11);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,12);
    
    /**
    5、配置 EXTI的触发方式（上升沿、下降沿或双边沿），
    这是导致中断无法触发的关键原因！
    10.2 External interrupt/event controller (EXTI)
    */
    EXTI_InitTypeDef EXTI_InitStructure;

   
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  // 下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    
    // 配置PB10（EXTI10）为下降沿触发（假设按键按下是低电平）
    EXTI_InitStructure.EXTI_Line = EXTI_Line10;
    EXTI_Init(&EXTI_InitStructure);

    // 配置PB11（EXTI11）和 PA11（EXTI11） 为下降沿触发
    EXTI_InitStructure.EXTI_Line = EXTI_Line11;
    EXTI_Init(&EXTI_InitStructure);

    // 配置PA12（EXTI12）
    EXTI_InitStructure.EXTI_Line = EXTI_Line12;
    EXTI_Init(&EXTI_InitStructure);
    
    NVIC_InitTypeDef nvic;
    nvic.NVIC_IRQChannel=EXTI15_10_IRQn;
    nvic.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&nvic);
}



/**
  * 函    数：按键获取键码
  * 参    数：无
  * 返 回 值：按下按键的键码值，范围：0~2，返回0代表没有按键按下
  * 注意事项：此函数是阻塞式操作，当按键按住不放时，函数会卡住，直到按键松手
  */
uint8_t Key_GetNum(void)
{
	uint8_t KeyNum = 0;		//定义变量，默认键码值为0
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)			//读PB1输入寄存器的状态，如果为0，则代表按键1按下
	{
		Delay_ms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);	//等待按键松手
		Delay_ms(20);											//延时消抖
		KeyNum = 1;												//置键码为1
	}
	
	if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)			//读PB11输入寄存器的状态，如果为0，则代表按键2按下
	{
		Delay_ms(20);											//延时消抖
		while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);	//等待按键松手
		Delay_ms(20);											//延时消抖
		KeyNum = 2;												//置键码为2
	}
	
	return KeyNum;			//返回键码值，如果没有按键按下，所有if都不成立，则键码为默认值0
}
